package com.abgame.test.websocket;

import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson2.JSONObject;
import jakarta.websocket.*;
import org.glassfish.tyrus.client.ClientManager;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
@ClientEndpoint
public class WebsocketListener {
    private String cookie;
    private int roomid = 1149306;
    private String token;

    public WebsocketListener(String cookie, int roomid, String token) {
        this.cookie = cookie;
        this.roomid = roomid;
        this.token = token;
    }

    public WebsocketListener() {

    }

    private Session session;

    public void connect() {
        try {
            // 使用 Tyrus 的 ClientManager 创建 WebSocket 连接
            ClientManager client = ClientManager.createClient();
            String wsUrl = "wss://broadcastlv.chat.bilibili.com/sub";
            client.connectToServer(this, new URI(wsUrl));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void connect2() {
//        BiliRequest biliReqest = new BiliRequest("2222");
//        JSONObject danmuInfoData = biliReqest.getDanmuInfoData(roomid);
//        //登录Token
//        String token = danmuInfoData.getString("token");
//        //选一个服务器节点
//        JSONArray hostList = danmuInfoData.getJSONArray("host_list");
//        JSONObject host = hostList.getJSONObject(0);
//        String wsUrl = String.format("ws://%s:%s/sub", host.getString("host"), host.getString("ws_port"));
        String wsUrl = "wss://broadcastlv.chat.bilibili.com/sub";
        //创建Websocket并连接
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        // 创建ClientManager实例
        // ClientManager clientManager = ClientManager.createClient();
        try {
            container.connectToServer(this, URI.create(wsUrl));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (DeploymentException e) {
            throw new RuntimeException(e);
        }
        // container.connectToServer(new WebsocketListener(cookie, roomId, token), new URI(wsUrl)); // 连接到WebSocket服务器
    }

    @OnOpen
    public void onOpen(Session session) throws IOException {
        this.session = session;
        RemoteEndpoint.Async remote = session.getAsyncRemote();
        //鉴权协议包
        ByteBuffer authPack = ByteBuffer.wrap(generateAuthPack());
        remote.sendBinary(authPack);
        //每30秒发送心跳包
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleAtFixedRate(() -> {
            try {
                ByteBuffer heartBeatPack = ByteBuffer.wrap(generateHeartBeatPack());
                remote.sendBinary(heartBeatPack);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }, 0, 20, TimeUnit.SECONDS);

    }

    @OnMessage
    public void onMessage(ByteBuffer byteBuffer) {
        //解包
        unpack(byteBuffer);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("断开连接: " + closeReason);
    }

    @OnError
    public void onError(Session session, Throwable t) {
        t.printStackTrace();
    }

    public interface Opt {
        short HEARTBEAT = 2;//	客户端发送的心跳包(30秒发送一次)
        short HEARTBEAT_REPLY = 3;//	服务器收到心跳包的回复 人气值，数据不是JSON，是4字节整数
        short SEND_SMS_REPLY = 5;//	服务器推送的弹幕消息包
        short AUTH = 7;//客户端发送的鉴权包(客户端发送的第一个包)
        short AUTH_REPLY = 8;//服务器收到鉴权包后的回复
    }

    public interface Version {
        short NORMAL = 0;//Body实际发送的数据——普通JSON数据
        short ZIP = 2; //Body中是经过压缩后的数据，请使用zlib解压，然后按照Proto协议去解析。
    }

    /**
     * 封包
     *
     * @param jsonStr 数据
     * @param code    协议包类型
     * @return
     * @throws IOException
     */
    public static byte[] pack(String jsonStr, short code) throws IOException {
        byte[] contentBytes = new byte[0];
        if (Opt.AUTH == code) {
            contentBytes = jsonStr.getBytes();
        }
        try (ByteArrayOutputStream data = new ByteArrayOutputStream();
             DataOutputStream stream = new DataOutputStream(data)) {
            stream.writeInt(contentBytes.length + 16);//封包总大小
            stream.writeShort(16);//头部长度 header的长度，固定为16
            stream.writeShort(Version.NORMAL);
            stream.writeInt(code);//操作码（封包类型）
            stream.writeInt(1);//sequence，可以取常数1 .保留字段，可以忽略。
            if (Opt.AUTH == code) {
                stream.writeBytes(jsonStr);
            }
            return data.toByteArray();
        }
    }


    /**
     * 生成认证包
     *
     * @return
     */
    public byte[] generateAuthPack(String jsonStr) throws IOException {
        return pack(jsonStr, Opt.AUTH);
    }

    /**
     * 生成认证包-用于非官方开放API
     *
     * @return
     */
    public byte[] generateAuthPack() throws IOException {
        JSONObject jo = new JSONObject();
//        Arrays.stream(cookie.split(";")).forEach(c -> {
//            if (c.trim().startsWith("DedeUserID=")) {
//                jo.put("uid", c.split("=")[1]);
//            } else if (c.trim().startsWith("buvid3=")) {
//                jo.put("buvid", c.split("=")[1]);
//            }
//        });
        jo.put("roomid", String.valueOf(roomid));
        jo.put("protover", Version.NORMAL);
        jo.put("platform", "web");
        jo.put("type", 2);
        //jo.put("key", token);
        return pack(jo.toString(), Opt.AUTH);
    }

    /**
     * 生成心跳包
     *
     * @return
     */
    public static byte[] generateHeartBeatPack() throws IOException {
        return pack(null, Opt.HEARTBEAT);
    }


    /**
     * 解包
     *
     * @param byteBuffer
     * @return
     */
    public static void unpack(ByteBuffer byteBuffer) {
        int packageLen = byteBuffer.getInt();
        short headLength = byteBuffer.getShort();
        short protVer = byteBuffer.getShort();
        int optCode = byteBuffer.getInt();
        int sequence = byteBuffer.getInt();
        if (Opt.HEARTBEAT_REPLY == optCode) {
            System.out.println("这是服务器心跳回复");
        }
        byte[] contentBytes = new byte[packageLen - headLength];
        byteBuffer.get(contentBytes);
        //如果是zip包就进行解包
        if (Version.ZIP == protVer) {
            unpack(ByteBuffer.wrap(ZipUtil.unZlib(contentBytes)));
            return;
        }

        String content = new String(contentBytes, StandardCharsets.UTF_8);
        if (Opt.AUTH_REPLY == optCode) {
            //返回{"code":0}表示成功
            System.out.println("这是鉴权回复：" + content);
        }
        //真正的弹幕消息
        if (Opt.SEND_SMS_REPLY == optCode) {
            System.out.println("真正的弹幕消息：" + content);
            // todo 自定义处理

        }
        //只存在ZIP包解压时才有的情况
        //如果byteBuffer游标 小于 byteBuffer大小，那就证明还有数据
        if (byteBuffer.position() < byteBuffer.limit()) {
            unpack(byteBuffer);
        }
    }
}
